package etxt2db.utils;

import java.io.*;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * An XML writer of UIMA Analysis Engine descriptors.
 * 
 * @author Gonçalo Simões
 * @author Rui Lageira
 * 
 */
public class AnalysisEngineDescriptorWriter {

	/**
	 * Uses an object holding information about the content of an UIMA Analysis Engine descriptor 
	 * to create the correspondent XML file.
	 * <p>
	 * @param data		the data object containing the content of the descriptor
	 * @param destPath	the directory where the descriptor will be saved
	 * @throws ParserConfigurationException	if a problem creating a {@link DocumentBuilder} occurs
	 * @throws TransformerException			if a problem creating a {@link Transformer} occurs
	 * @throws IOException					if an input/output error occurs
	 */
	public static void write(AnalysisEngineDescriptorData data, String destPath) throws ParserConfigurationException, TransformerException, IOException {
		
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = factory.newDocumentBuilder();
		
		Document descriptor = builder.newDocument();
		
		descriptor.appendChild(createAnalysisEngineDescription(descriptor,data));
		
		DOMSource source = new DOMSource(descriptor);
		
		new File(destPath+"/").mkdir();
		
		File xmlFile = new File(destPath+"/"+data.getName()+".xml");
		
		PrintStream ps = new PrintStream(xmlFile);
		StreamResult result = new StreamResult(ps);
		
		TransformerFactory transformerFactory = TransformerFactory.newInstance();
		transformerFactory.setAttribute("indent-number", 10);
		Transformer transformer = transformerFactory.newTransformer();
		transformer.setOutputProperty(OutputKeys.INDENT, "yes");
		
		transformer.transform(source, result);
		
	}
	
	/**
	 * Creates the root XML element of an AE descriptor, tagged with the 
	 * name analysisEngineDescription.
	 * <p>
	 * @param descriptor	the XML {@link Document}
	 * @param data			the data object containing the content of the descriptors' elements
	 * @return				the analysisEngineDescription {@link Element}
	 * 
	 * @see http://uima.apache.org/downloads/releaseDocs/2.1.0-incubating/docs/html/tutorials_and_users_guides/tutorials_and_users_guides.html#ugr.tug.aae.xml_intro_ae_descriptor
	 */
	private static Element createAnalysisEngineDescription(Document descriptor, AnalysisEngineDescriptorData data) {
		
		Element analysisEngineDescription = descriptor.createElement("analysisEngineDescription");
		analysisEngineDescription.setAttribute("xmlns", data.getNamespace());
		
		Element frameworkImplementation = descriptor.createElement("frameworkImplementation");
		frameworkImplementation.setTextContent(data.getFrameworkImplementation());
		
		Element primitive = descriptor.createElement("primitive");
		primitive.setTextContent(data.getPrimitive());
		
		Element annotatorImplementationName = descriptor.createElement("annotatorImplementationName");
		annotatorImplementationName.setTextContent(data.getAnnotatorImplementationName());
		
		analysisEngineDescription.appendChild(frameworkImplementation);
		analysisEngineDescription.appendChild(primitive);
		analysisEngineDescription.appendChild(annotatorImplementationName);
		analysisEngineDescription.appendChild(createAnalysisEngineMetaData(descriptor,data));
		analysisEngineDescription.appendChild(descriptor.createElement("resourceManagerConfiguration"));
		
		return analysisEngineDescription;
	}

	/**
	 * TODO
	 * <p>
	 * @param descriptor	the XML {@link Document}
	 * @param data			the data object containing the content of the descriptors' elements
	 * @return				the analysisEngineMetaData {@link Element}
	 * 
	 * @see http://uima.apache.org/downloads/releaseDocs/2.1.0-incubating/docs/html/tutorials_and_users_guides/tutorials_and_users_guides.html#ugr.tug.aae.xml_intro_ae_descriptor
	 */
	private static Element createAnalysisEngineMetaData(Document descriptor, AnalysisEngineDescriptorData data) {
		
		Element analysisEngineMetaData = descriptor.createElement("analysisEngineMetaData");
		
		Element name = descriptor.createElement("name");
		name.setTextContent(data.getName());

		Element description = descriptor.createElement("description");
		description.setTextContent(data.getDescription());

		Element version = descriptor.createElement("version");
		version.setTextContent(data.getVersion());

		Element vendor = descriptor.createElement("vendor");
		vendor.setTextContent(data.getVendor());
		
		analysisEngineMetaData.appendChild(name);
		analysisEngineMetaData.appendChild(description);
		analysisEngineMetaData.appendChild(version);
		analysisEngineMetaData.appendChild(vendor);
		analysisEngineMetaData.appendChild(createConfigurationParameters(descriptor,data));
		analysisEngineMetaData.appendChild(createConfigurationParameterSettings(descriptor,data));
		analysisEngineMetaData.appendChild(createTypeSystemDescription(descriptor,data));
		analysisEngineMetaData.appendChild(descriptor.createElement("typePriorities"));
		analysisEngineMetaData.appendChild(descriptor.createElement("fsIndexCollection"));
		analysisEngineMetaData.appendChild(descriptor.createElement("typePriorities"));
		analysisEngineMetaData.appendChild(createCapabilities(descriptor,data));
		analysisEngineMetaData.appendChild(createOperationalProperties(descriptor,data));
		
		return analysisEngineMetaData;
	}

	/**
	 * TODO
	 * <p>
	 * @param descriptor	the XML {@link Document}
	 * @param data			the data object containing the content of the descriptors' elements
	 * @return				the configurationParameters {@link Element}
	 * 
	 * @see http://uima.apache.org/downloads/releaseDocs/2.1.0-incubating/docs/html/tutorials_and_users_guides/tutorials_and_users_guides.html#ugr.tug.aae.xml_intro.configuration_parameters
	 */
	private static Element createConfigurationParameters(Document descriptor, AnalysisEngineDescriptorData data) {
		
		Element configurationParameters = descriptor.createElement("configurationParameters");
		
		configurationParameters.appendChild(newConfigurationParameter(descriptor,"ModelPath", "String", "false", "true"));
		configurationParameters.appendChild(newConfigurationParameter(descriptor,"Attributes", "String", "true", "true"));
		configurationParameters.appendChild(newConfigurationParameter(descriptor,"Mappings", "String", "true", "true"));
		
		return configurationParameters;
	}
	
	/**
	 * TODO
	 * <p>
	 * @param descriptor	the XML {@link Document}
	 * @param name			the name of the parameter
	 * @param type			the type of the parameter
	 * @param multiValued	boolean as string indicating whether or not the parameter has multiple values
	 * @param mandatory		boolean as string indicating whether or not it is mandatory to assign a value for the parameter
	 * @return 				a new configurationParameter {@link Element}
	 * 
	 * @see http://uima.apache.org/downloads/releaseDocs/2.1.0-incubating/docs/html/tutorials_and_users_guides/tutorials_and_users_guides.html#ugr.tug.aae.xml_intro.configuration_parameters
	 */
	private static Element newConfigurationParameter(Document descriptor, String name, String type, String multiValued, String mandatory) {
		
		Element confParameter = descriptor.createElement("configurationParameter");
		
		Element nameEl = descriptor.createElement("name");
		nameEl.setTextContent(name);
		
		Element typeEl = descriptor.createElement("type");
		typeEl.setTextContent(type);
		
		Element multiValuedEl = descriptor.createElement("multiValued");
		multiValuedEl.setTextContent(multiValued);
		
		Element mandatoryEl = descriptor.createElement("mandatory");
		mandatoryEl.setTextContent(mandatory);
		
		confParameter.appendChild(nameEl);
		confParameter.appendChild(typeEl);
		confParameter.appendChild(multiValuedEl);
		confParameter.appendChild(mandatoryEl);
		
		return confParameter;
	}
	
	/**
	 * TODO
	 * <p>
	 * @param descriptor	the XML {@link Document}
	 * @param data			the data object containing the content of the descriptors' elements
	 * @return				the configurationParameterSettings {@link Element}
	 * 
	 * @see http://uima.apache.org/downloads/releaseDocs/2.1.0-incubating/docs/html/tutorials_and_users_guides/tutorials_and_users_guides.html#ugr.tug.aae.xml_intro.configuration_parameters
	 */
	private static Element createConfigurationParameterSettings(Document descriptor, AnalysisEngineDescriptorData data) {
		
		Element configurationParameterSettings = descriptor.createElement("configurationParameterSettings");
		
		configurationParameterSettings.appendChild(newConfigurationParameterSetting(descriptor, "ModelPath", data.getModelPath(), "string"));
		configurationParameterSettings.appendChild(newConfigurationParameterSetting(descriptor, "Attributes", data.getAttributes(), "string"));
		configurationParameterSettings.appendChild(newConfigurationParameterSetting(descriptor, "Mappings", data.getMappingsFileContent(), "string"));
		
		return configurationParameterSettings;
	}
	
	/**
	 * TODO
	 * <p>
	 * @param descriptor	the XML {@link Document}
	 * @param name			the name of the parameter
	 * @param value			the value for the parameter
	 * @param type			the type of the parameter
	 * @return				the nameValuePair {@link Element}
	 * 
	 * @see http://uima.apache.org/downloads/releaseDocs/2.1.0-incubating/docs/html/tutorials_and_users_guides/tutorials_and_users_guides.html#ugr.tug.aae.xml_intro.configuration_parameters
	 */
	private static Element newConfigurationParameterSetting(Document descriptor, String name, String value, String type) {
		
		Element nameValuePair = descriptor.createElement("nameValuePair");
		
		Element nameEl = descriptor.createElement("name");
		nameEl.setTextContent(name);
		
		Element valueTag = descriptor.createElement("value");

		Element valueEl = descriptor.createElement(type);
		valueEl.setTextContent(value);
		valueTag.appendChild(valueEl);
		
		nameValuePair.appendChild(nameEl);
		nameValuePair.appendChild(valueTag);
		
		return nameValuePair;
	}
	
	/**
	 * TODO
	 * <p>
	 * @param name		the name of the parameter
	 * @param value		the list of values for the parameter
	 * @param type		the type of the parameter
	 * @return			the nameValuePair {@link Element}
	 * 
	 * @see http://uima.apache.org/downloads/releaseDocs/2.1.0-incubating/docs/html/tutorials_and_users_guides/tutorials_and_users_guides.html#ugr.tug.aae.xml_intro.configuration_parameters
	 */
	private static Element newConfigurationParameterSetting(Document descriptor, String name, List<String> values, String type) {
		
		Element nameValuePair = descriptor.createElement("nameValuePair");
		
		Element nameEl = descriptor.createElement("name");
		nameEl.setTextContent(name);
		
		Element valueTag = descriptor.createElement("value");

		Element array = descriptor.createElement("array");
		
		for(String v : values) {
			Element valueEl = descriptor.createElement(type);
			valueEl.setTextContent(v);
			array.appendChild(valueEl);	
		}
		
		valueTag.appendChild(array);
			
		nameValuePair.appendChild(nameEl);
		nameValuePair.appendChild(valueTag);
		
		return nameValuePair;
	}
	
	/**
	 * TODO
	 * <p>
	 * @param descriptor	the XML {@link Document}
	 * @param data			the data object containing the content of the descriptors' elements
	 * @return				the typeSystemDescription {@link Element}
	 * 
	 * @see http://uima.apache.org/downloads/releaseDocs/2.1.0-incubating/docs/html/tutorials_and_users_guides/tutorials_and_users_guides.html#ugr.tug.aae.xml_intro_type_system_definition
	 */
	private static Element createTypeSystemDescription(Document descriptor, AnalysisEngineDescriptorData data) {
		
		Element typeSystemDescription = descriptor.createElement("typeSystemDescription");
		
		Element imports = descriptor.createElement("imports");
		
		Element import_uc = descriptor.createElement("import");
		import_uc.setAttribute("name", data.getUCompareTypeSystem());
		
		imports.appendChild(import_uc);
		
		Element types = descriptor.createElement("types");
		
		Element typeDescription = descriptor.createElement("typeDescription");
		
		Element name = descriptor.createElement("name");
		name.setTextContent("etxt2db.annotators.NamedEntityTyped");
		
		Element description = descriptor.createElement("description");
		
		Element supertypeName = descriptor.createElement("supertypeName");
		supertypeName.setTextContent("org.u_compare.shared.semantic.NamedEntity");	
		
		Element features = descriptor.createElement("features");
		Element featureDescription = descriptor.createElement("featureDescription");
		Element featureName = descriptor.createElement("name");
		featureName.setTextContent("tag");
		Element featureDesc = descriptor.createElement("description");
		Element featureRange = descriptor.createElement("rangeTypeName");
		featureRange.setTextContent("uima.cas.String");
		
		featureDescription.appendChild(featureName);
		featureDescription.appendChild(featureDesc);
		featureDescription.appendChild(featureRange);
		features.appendChild(featureDescription);
		
		typeDescription.appendChild(name);
		typeDescription.appendChild(description);
		typeDescription.appendChild(supertypeName);
		typeDescription.appendChild(features);
		
		types.appendChild(typeDescription);
		
		typeSystemDescription.appendChild(imports);
		typeSystemDescription.appendChild(types);
		
		return typeSystemDescription;
		
	}
	

	/**
	 * TODO
	 * <p>
	 * @param descriptor	the XML {@link Document}
	 * @param data			the data object containing the content of the descriptors' elements
	 * @return				the capabilities {@link Element}
	 * 
	 * @see http://uima.apache.org/downloads/releaseDocs/2.1.0-incubating/docs/html/tutorials_and_users_guides/tutorials_and_users_guides.html#ugr.tug.aae.xml_intro_capabilities
	 */
	private static Element createCapabilities(Document descriptor, AnalysisEngineDescriptorData data) {
		
		Element capabilities = descriptor.createElement("capabilities");
		
		capabilities.appendChild(createCapability(descriptor,data.getInputTypes(),data.getOutputTypes()));
		
		return capabilities;
	}
	
	/**
	 * TODO
	 * <p>
	 * @param descriptor	the XML {@link Document}
	 * @param inputTypes	the list of input types of the Analysis Engine
	 * @param outputTypes	the list of output types of the Analysis Engine
	 * @return				the capability {@link Element}
	 * 
	 * @see http://uima.apache.org/downloads/releaseDocs/2.1.0-incubating/docs/html/tutorials_and_users_guides/tutorials_and_users_guides.html#ugr.tug.aae.xml_intro_capabilities
	 */
	private static Element createCapability(Document descriptor, List<String> inputTypes, List<String> outputTypes) {
		
		Element capability = descriptor.createElement("capability");
		
		Element inputs = descriptor.createElement("inputs");
		
		for(String input : inputTypes) {
			Element type = descriptor.createElement("type");
			type.setAttribute("allAnnotatorFeatures", "true");
			type.setTextContent(input);	
			inputs.appendChild(type);
		}
		
		Element outputs = descriptor.createElement("outputs");
		
		for(String output : outputTypes) {
			Element type = descriptor.createElement("type");
			type.setAttribute("allAnnotatorFeatures", "true");
			type.setTextContent(output);	
			outputs.appendChild(type);
		}
		
		capability.appendChild(inputs);
		capability.appendChild(outputs);
		capability.appendChild(descriptor.createElement("languagesSupported"));
		
		return capability;
		
	}
	
	/**
	 * TODO
	 * <p>
	 * @param descriptor	the XML {@link Document}
	 * @param data			the data object containing the content of the descriptors' elements
	 * @return				the operationalProperties {@link Element}
	 * @see	http://uima.apache.org/downloads/releaseDocs/2.1.0-incubating/docs/html/tutorials_and_users_guides/tutorials_and_users_guides.html#ugr.tug.cpe.collection_reader.developing
	 */
	private static Element createOperationalProperties(Document descriptor, AnalysisEngineDescriptorData data) {
		
		Element operationalProperties = descriptor.createElement("operationalProperties");
		
		Element modifiesCas = descriptor.createElement("modifiesCas");
		modifiesCas.setTextContent(data.getModifiesCas());
		
		Element multipleDeploymentAllowed = descriptor.createElement("multipleDeploymentAllowed");
		multipleDeploymentAllowed.setTextContent(data.getMultipleDeploymentAllowed());
		
		Element outputsNewCASes = descriptor.createElement("outputsNewCASes");
		outputsNewCASes.setTextContent(data.getOutputsNewCASes());
		
		operationalProperties.appendChild(modifiesCas);
		operationalProperties.appendChild(multipleDeploymentAllowed);
		operationalProperties.appendChild(outputsNewCASes);
		
		return operationalProperties;
		
	}
	
}
